package cn.edu.nju.ws.aquila.rdf.sentence;


import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import com.hp.hpl.jena.ontology.OntDocumentManager;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntModelSpec;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.RDFReader;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;

/**
 * Parse a Semantic Web document to RDF sentences
 * 
 * @author Gong Cheng
 * 
 */
public class RDFSentenceParser {
	/* All the sentences */
	private ArrayList<RDFSentence> sentences = null;

	/* Jena OntModel */
	private OntModel ontModel = null;

	public RDFSentenceParser(OntModel ontModel) {
		this.ontModel = ontModel;
		this.constructSentences();
	}

	/**
	 * Construct sentences
	 * 
	 */
	private void constructSentences() {
		this.sentences = new ArrayList<RDFSentence>();

		HashMap<RDFNode, HashSet<Statement>> bnode2Stmts = new HashMap<RDFNode, HashSet<Statement>>();
		HashSet<Statement> remainingStmts = new HashSet<Statement>();

		/* Vertices */
		for (StmtIterator allStmts = this.ontModel.listStatements(); allStmts
				.hasNext();) {
			Statement stmt = allStmts.nextStatement();

			RDFNode subj = stmt.getSubject();
			if (subj.isAnon()) {
				HashSet<Statement> stmts = bnode2Stmts.get(subj);
				if (stmts == null) {
					stmts = new HashSet<Statement>();
					bnode2Stmts.put(subj, stmts);
				}
				stmts.add(stmt);
			}

			RDFNode obj = stmt.getObject();
			if (obj.isAnon()) {
				HashSet<Statement> stmts = bnode2Stmts.get(obj);
				if (stmts == null) {
					stmts = new HashSet<Statement>();
					bnode2Stmts.put(obj, stmts);
				}
				stmts.add(stmt);
			}

			remainingStmts.add(stmt);
		}

		while (!remainingStmts.isEmpty()) {
			Statement stmt = remainingStmts.iterator().next();
			HashSet<Statement> visitedStmts = new HashSet<Statement>();
			HashSet<Statement> unvisitedStmts = new HashSet<Statement>();
			unvisitedStmts.add(stmt);

			while (!unvisitedStmts.isEmpty()) {
				Iterator<Statement> stmts = unvisitedStmts.iterator();
				Statement currStmt = stmts.next();
				stmts.remove();

				visitedStmts.add(currStmt);

				RDFNode subj = currStmt.getSubject();
				if (subj.isAnon()) {
					for (Statement nextStmt : bnode2Stmts.get(subj)) {
						if (!visitedStmts.contains(nextStmt)) {
							unvisitedStmts.add(nextStmt);
						}
					}
				}
				RDFNode obj = currStmt.getObject();
				if (obj.isAnon()) {
					for (Statement nextStmt : bnode2Stmts.get(obj)) {
						if (!visitedStmts.contains(nextStmt)) {
							unvisitedStmts.add(nextStmt);
						}
					}
				}
			}

			remainingStmts.removeAll(visitedStmts);

			RDFSentence sentence = new RDFSentence();
			for (Statement aStmt : visitedStmts) {
				sentence.addStatement(aStmt);
			}
			this.sentences.add(sentence);

			visitedStmts.clear();
			visitedStmts = null;
			unvisitedStmts.clear();
			unvisitedStmts = null;
		}

		bnode2Stmts.clear();
		bnode2Stmts = null;
		remainingStmts.clear();
		remainingStmts = null;
	}

	/**
	 * Return all the sentences
	 * 
	 * @return All the sentences
	 */
	public ArrayList<RDFSentence> getSentences() {
		return this.sentences;
	}

	/**
	 * Return the Jena OntModel
	 * 
	 * @return Jena OntModel
	 */
	public OntModel getJenaOntModel() {
		return this.ontModel;
	}

	/**
	 * Print all the RDF sentences to System.out
	 * 
	 */
	public void display() {
		for (RDFSentence sentence : this.sentences) {
			sentence.display();
			System.out.println();
		}
	}

	public void close() {
		for (RDFSentence rdfSentence : this.sentences) {
			rdfSentence.close();
		}
		this.sentences.clear();
		this.sentences = null;
		this.ontModel.close();
		this.ontModel = null;
	}
}